#include <stdio.h>
#include <string.h>
#include <unistd.h>

int word_char(char c)
{
    if ((c >= '0' && c <='9') ||
            (c >= 'a' && c <= 'z') ||
            (c >= 'A' && c <= 'Z') ||
            c == '_') return 1;
    return 0;
}

int check_con(char *begin, char *end)
{
    while (begin != end + 1) {
        if (word_char(*begin) == 0) return 0;
        begin++;
    }
    return 1;
}

int con_str(char *s, char *begin, char *end)
{
    int n = 0;
    while (begin != end + 1) {
        *s = *begin;
        s++;
        begin++;
        n++;
    }
    return n;
}

char *g_skip_s = NULL;

int skip(char *s)
{
    if (g_skip_s && strstr(s, g_skip_s)) return 1;
    return 0;
}

char * match_pattern(char **oline, char *begin, char *s, int con, const char *pattern)
{
    char _pattern[256];
    char *op = *oline;
    char *cbegin = s;
    char *p = _pattern, *np, *t, *first_match = NULL, *last_end = NULL;
    char c;
    char *q;
    int wildcard = 0;
    int match = 0;

    if (s && *s) match = 1;
    strcpy(_pattern, pattern);
    while (match && s && *s) {
        for (p = _pattern;match && p && *p;p = np, *np = c) {
            if (*p == '*') for (np = p;np && *np && *np == '*';np++);/* find next pattern */
            else for (np = p;np && *np && *np != '*';np++);
            c = *np;
            *np = 0;
            if (*p == '*') {
                wildcard = 1;
                continue;
            }
            while (match) {
                t = strstr(s, p);
                if (t) {
                    if (last_end != NULL) {
                        if (check_con(last_end, t - 1)) {
                            last_end = t + strlen(p);
                            wildcard = 0;
                            break;/* match OK */
                        }
                    } else {
                        first_match = t;
                        last_end = t + strlen(p);
                        if (wildcard || (t == begin) || !word_char(*(t - 1)) || !word_char(*t)) {
                            wildcard = 0;
                            break;/* match OK */
                        }
                    }
                } else if (!first_match) {
                    match = 0;
                }
                /* restart match */
                p = NULL;
                wildcard = 0;
                break;
            }
            if (!p) {
                *np = c;
                break;
            }
        }
        if (match) {
            if (p && (wildcard || !word_char(*(last_end)) || !word_char(*(last_end - 1)))) break;/* if p is not 0, the match finishes normally */
            op += con_str(op, s, first_match);
            s = first_match + 1;
            first_match = NULL;
            last_end = NULL;
            p = NULL;
        }
    }
    if (match && p) {
        if (!first_match) {
            for (first_match = cbegin;first_match && *first_match;first_match++) {
                if (*first_match != ' ') break;
            }
        }
        if (!first_match || !(*first_match)) return NULL;
        if (!last_end) last_end = first_match + 1;
        while (first_match != cbegin && word_char(*(first_match - 1)) && (word_char(*first_match) || pattern[0] == '*')) first_match--;
        while (word_char(*(last_end)) && wildcard) last_end++;
        if (con) {
            c = 0;
            for (q = cbegin;q != first_match;q++) {
                if (*q == '*') c++;
                if ((*q != ' ' && *q != '\t' && *q != '*') || c > 2) {
                    return NULL;
                }
            }
        }
        if (first_match > s) {
            op += con_str(op, s, first_match - 1);
        } else {
            op -= (s - first_match);
        }
        op += sprintf(op, "\033[1m\033[40;33m");
        op += con_str(op, first_match, last_end - 1);
        op += sprintf(op, "\033[0m");
        *oline = op;
        return last_end;
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    char cwd[1024];
    getcwd(cwd, sizeof(cwd));
    FILE *fp = popen("find -name \"*\"", "r");
    char fname[1024];
    char line[1024];
    char oline[2048];
    char pattern[256];
    char c;
    char *nl, *t, *p, *q;
    FILE *f;
    int lno;
    int match;
    int i;
    int con;

    printf("cwd = %s\n", cwd);

    if (fp ==NULL) {
        printf("Error open\n");
        return __LINE__;
    }

    if (argc == 1) return 0;
    if (!strcmp(argv[0], "findd")) g_skip_s = "=";
    printf("Searching for [");
    for (i = 1;i < argc;i++) {
        printf(" %s", argv[i]);
    }
    printf(" ]\n");
    printf("--------------------------------------------------\n");
    while (fgets(fname, sizeof(fname), fp) != NULL) {
        if (strstr(fname, "/.")) continue;
        t = strstr(fname, "\n");
        if (t) *t = 0;
        f = fopen(fname, "r");
        if (f) {
            lno = 0;
            while (fgets(line, sizeof(line), f) != NULL) {
                if (skip(line)) continue;
                match = 1;
                for (i = 0;i < sizeof(line);i++) {
                    if (line[i] == 0) break;
                    if (line[i] > 127) {
                        match = 0;
                        break;
                    }
                }
                if (!match) break;
                t = strstr(line, "\n");
                if (t) *t = 0;
                t = strstr(line, "\r");
                if (t) *t = 0;
                lno++;
                t = oline;
                nl = line;
                con = 0;
                for (i = 1;i < argc;i++) {
                    if (!strcmp(argv[i], "+")) {
                        con = 0;
                        continue;
                    }
                    nl = match_pattern(&t, line, nl, con, argv[i]);
                    con = 1;
                    if (!nl) break;
                }
                if (i == argc) {
                    printf("%s%s:%d\t%s%s\r\n", cwd, fname + 1, lno, oline, nl);
                }
            }
            fclose(f);
        }
    }
#if 0

                for (i = 0;i < sizeof(line);i++) {
                    if (line[i] == 0) break;
                    if (line[i] != '\r' && line[i] != '\n' && (line[i] < ' ' || line[i] > 127)) {
                        match = 0;
                        break;
                    }
                }
                if (!match) break;
                p = oline;
                nl = line;
                for (i = 1;i < argc;i++) {
                    if (argv[i][0] == '`') {
                        strcpy(pattern, argv[i] + 1);
                        t = strstr(nl, pattern);
                        for (q = nl;t && q != t;q++) {
                            if (*q != ' ' && *q != '\t') t = NULL;
                        }
                    } else if (argv[i][strlen(argv[i]) - 1] == '`') {
                        strcpy(pattern, argv[i]);
                        pattern[strlen(pattern) - 1] = 0;
                        t = strstr(nl, pattern);
                        for (q = t + strlen(pattern);t && *q;q++) {
                            if (*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') t = NULL;
                        }
                    } else if (argv[i][0] == 0) {
                        pattern[0] = 0;
                        for (t = nl;t && *t;t++) {
                            if (*t != ' ' && *t != '\t') break;
                        }
                        for (;t && *t;t++) {
                            if (*t == ' ' || *t == '\t') break;
                        }
                    } else {
                        strcpy(pattern, argv[i]);
                        t = strstr(nl, pattern);
                    }
                    if (t) {
                        c = *t;
                        *t = 0;
                        if (nl[0]) p += sprintf(p, "%s", nl);
                        if (pattern[0]) {
                            p += sprintf(p, "\033[1m%s\033[0m", pattern);
                            nl = t + strlen(pattern);
                        } else {
                            nl = t;
                            *t = c;
                        }
                    } else {
                        match = 0;
                        break;
                    }
                }
                if (match) {
                    p += sprintf(p, "%s", nl);
                    printf("%s +%d\t%s", fname, lno, oline);
                }
            }
            fclose(f);
        }
    }
#endif
    pclose(fp);
    return 0;
}
